#pragma once

#include "console_tester.h"

////////////////////////////////////////
#include "zen_bezier.h"
#include "zen_random.h"
void debug_bezier()
{
    Zen::Bezier2Dx4 b;
    b.SetControl(0, { 0, 0 });
    b.SetControl(1, { 0, 1 });
    b.SetControl(2, { 1, 1 });
    b.SetControl(3, { 1, 0 });
    for (float v = 0; v < 1.0; v += 0.05f)
    {
        cout << v << " :  " << b.GetPoint(v) << endl;
    }
}
void debug_bezier3d()
{
    Zen::Bezier3Dx3 b3dx3;
    b3dx3.SetControl(0, { 0, 0, 0 });
    b3dx3.SetControl(1, { 0, 1, 0 });
    b3dx3.SetControl(2, { 0, 0, 2 });

    for (int i = 0; i < b3dx3.ControlCount; ++i)
    {
        cout << "ctrl " << i << "  :   " << b3dx3.GetControl(i) << endl;
    }

    for (float v = 0; v < 1.0; v += 0.1f)
    {
        cout << v << " :  " << b3dx3.GetPoint(v) << endl;
    }
}
void debug_bezierx()
{
    Zen::Random r;
    Zen::BezierBase<Point2, 6> b; // with 6 ctrl point.
    for (int i = 0; i < b.ControlCount; ++i)
    {
        b.SetControl(i, { r.NextF(100.f), r.NextF(100.f) });
    }
    for (int i = 0; i < b.ControlCount; ++i)
    {
        cout << "ctrl " << i << "  :   " << b.GetControl(i) << endl;
    }
    for (float v = 0; v < 1.0; v += 0.01f)
    {
        cout << v << " :  " << b.GetPoint(v) << endl;
    }
}
AddTestQ(bezier);
AddTestQ(bezier3d);
AddTestQ(bezierx);

////////////////////////////////////////
#include "zen_buffer.h"

void debug_buffer()
{
    std::vector<uint8_t> b;
    Zen::BufferWriter bw(b);

    bw.WriteString("hello", false);
    bw.Write("world", 5);
    bw.Write(120);
    bw.Write((char)0);
    std::cout << b.size() << endl;
    std::cout << string(b.size(), '.') << endl;
    std::cout << b.data() << endl;
    bw.MovePutter(-5);
    bw.Write("haha", 4);
    std::cout << b.size() << endl;
    std::cout << string(b.size(), '.') << endl;
    std::cout << b.data() << endl;

    bw.SetPutter(5);
    bw.WriteString("pettery");
    std::cout << b.size() << endl;
    std::cout << string(b.size(), '.') << endl;
    std::cout << b.data() << endl;
}
AddTestQ(buffer);

////////////////////////////////////////
#include "zen_endian.h"
template <typename F>
int calc(F f)
{
    int total = 0;
    for (int i = 0; i < 100000; ++i)
    {
        total += f(i);
    }
    return total;
}

#if ZEN_OS_WIN
#include <Windows.h>
#include <winsock.h>
#pragma comment(lib, "ws2_32.lib")
#undef min
#undef max
#endif

void debug_endian()
{
    int32_t v = 1234;
    PRINTER(v, "EndianSwap32:", EndianSwap32(v), "EndianSwap16:", EndianSwap16(v), "EndianSwap32^2:", EndianSwap32(EndianSwap32(v)), "HostNet32:", HostNet32(v));

    Byte4 b4;
    b4.SetWithBE(v);
    PRINTER(v, "SetWithBE:", b4.value, "GetBEValue:", b4.GetBEValue(), "GetLEValue:", b4.GetLEValue());

    {
        auto v1 = calc(&htonl);
        auto v2 = calc(&Zen::HostNet32);
        PRINTER("htonl:", v1, "HostNet32:", v2);
    }

    {
        auto v1 = calc(&htons);
        auto v2 = calc(&Zen::HostNet16);
        PRINTER("htons:", v1, "HostNet16:", v2);
    }
}
AddTestQ(endian);

////////////////////////////////////////
#include "zen_fraction.h"
void _put(Fraction const& f)
{
    cout << f << "=" << f.GetValue() << endl;
}
void debug_fraction()
{
    _put(Fraction(1, 200));
    _put(Fraction(-100, 200));
    _put(Fraction(100, 201));
    _put(Fraction(10000, -200));
    _put(Fraction(-2, -30));

    Fraction a(-20, 9), b(3, 8), c(-3, 7), d(-7, -5), e(18, 0);

#define P(a) PRINTER(#a, "[", (a), "] =", (a).GetValue())
    P(a);
    P(b);
    P(c);
    P(d);
    P(e);
    P(a + b);
    P(a - c);
    P(a * c);
    P(a / d);
    P(a + e);
    P(a - e);
    P(a * e);
    P(a / e);
#undef P
}
AddTestQ(fraction);

////////////////////////////////////////
#include "zen_number.h"
void debug_lerp()
{
    float a = 100, b = 0;

    float end = 100.f;
    float prev[2] = { end, end };
    for (float i = 0; i < 100.f; i += 1)
    {
        auto c1 = Number::LerpDown(a, b, i / end);
        auto c2 = Number::Lerp(a, b, i / end);
        PRINTER(c1, c2, prev[0] - c1, prev[1] - c2);
        prev[0] = c1;
        prev[1] = c2;
    }
}
AddTestQ(lerp);

////////////////////////////////////////
void debug_gcd()
{
    cout << "input two number to calc gcd:" << endl;
    int a{}, b{};
    cin.clear();
    cin >> a >> b;
    cout << "gcd of " << a << " and " << b << " IS " << Number::GetGCD(a, b) << endl;
}
AddTestQ(gcd);

////////////////////////////////////////
void debug_ticker()
{
    auto now = std::chrono::system_clock::now();
    PRINTER(Zen::Time::ToSeconds(std::chrono::system_clock::now().time_since_epoch()));
    auto diff = std::chrono::system_clock::now() - now;
    PRINTER(Zen::Time::ToSeconds(diff));
}
AddTest("ticker", debug_ticker);

/// MinPower2
////////////////////////////////////////////////
#include "zen_number.h"
#define MINPOWER_N 10000000
void debug_minpower()
{
    {
        int v = 0;
        while (std::cin >> v && v >= 0)
        {
            PRINTER(Zen::Number::MinPower2NoLess(v));
        }
    }

    {
        int64_t v = 0;
        while (std::cin >> v && v >= 0)
        {
            PRINTER(Zen::Number::MinPower2NoLess(v));
        }
    }
    int64_t res = 0;
    for (uint64_t i = 0; i < MINPOWER_N; ++i)
    {
        res += Zen::Number::MinPower2NoLess(i);
    }
    PRINTER(res);
}

AddTest("minpower", debug_minpower);

////////////////////////////////////////////////
#include "zen_random.h"
void debug_random()
{
    Random r;
    static const int Limit = 100;
    std::vector<int> counts(Limit, 0);
    forrepeat(i, 120)
    {
        auto n = r.Next() % Limit;
        counts[n]++;
    }
    for (auto& v : counts)
    {
        cout << v << endl;
    }
}
AddTest("random", debug_random);

////////////////////////////////////////////////
#include "zen_cache.h"
class CacheTester : public Zen::Cache<int, int>
{
  public:
    virtual std::shared_ptr<int> LoadData(int key)
    {
        return std::make_shared<int>(key * key);
    }
};
